package com.zhiwang.xiaoqu.ui

import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.zhiwang.xiaoqu.R
import com.zhiwang.xiaoqu.util.ReflectUtil
import kotlinx.android.synthetic.main.activity_base_list.*
import java.lang.reflect.Constructor
import java.lang.reflect.ParameterizedType

/**
 * 通用列表页面，处理公共的上拉加载更多下拉刷新 和 数据刷新等逻辑；子类只需要处理数据获取以及必要的 ViewHolder 的编写，
 *这里 ViewHolder 用了反射的方式创建，由于各列表 布局并不复杂，这里并不会影响太多性能
 */
abstract class BaseListActivity<T, VH : RecyclerView.ViewHolder> : BaseActivity() {

    val baseDatas = arrayListOf<T>()

    lateinit var baseListAdapter: BaseListAdapter

    var viewHolderConstructor: Constructor<*>? = null

    var emptyView: View? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_base_list)

        toolbar.title = getToolBarTitle()

        setSupportActionBar(toolbar)

        recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
        baseListAdapter = BaseListAdapter()
        recyclerView.adapter = baseListAdapter

        smartRefreshLayout.setOnLoadMoreListener {
            startLoadMore()
        }

        smartRefreshLayout.setOnRefreshListener {
            startRefresh()
        }
        hideOrShowEmptyView(baseDatas.isEmpty())
    }

    fun recyclerView() = recyclerView

    fun smartRefreshLayout() = smartRefreshLayout


    override fun onSupportNavigateUp(): Boolean {
        onBackPressed()
        return true
    }

    abstract fun startLoadMore()

    abstract fun startRefresh()

    abstract fun getToolBarTitle(): String


    /**
     * 实际上 通过继承方式 返回 layoutId 并不合适
     */

    @LayoutRes
    abstract fun getLayoutRes(): Int


    open fun getEmptyLayoutRes() = 0

    /**
     * 有些列表会有头部，但是 是静态的，不需要数据绑定，所以在写有头部的页面的 ViewHolder 时候 需要判断一下是否有头部
     */
    @LayoutRes
    open fun getHeaderLayoutRes() = 0

    fun onRefreshFinish() {
        baseDatas.clear()
        baseListAdapter.notifyDataSetChanged()
    }


    fun onLoadMoreData(datas: List<T>) {
        val oldList = arrayListOf<T>()
        oldList.addAll(baseDatas)
        baseDatas.addAll(datas)

        DiffUtil.calculateDiff(DiffCallBackImpl(oldList, baseDatas)).dispatchUpdatesTo(baseListAdapter)
        stopLoadMore()
        hideOrShowEmptyView(baseDatas.isEmpty())
        if (datas.isNotEmpty())
            onLoadMoreComplete()
    }

    open fun onLoadMoreComplete() {

    }


    fun onRefreshData(datas: List<T>) {
        val oldList = arrayListOf<T>()
        oldList.addAll(baseDatas)
        baseDatas.clear()
        baseDatas.addAll(datas)
        DiffUtil.calculateDiff(DiffCallBackImpl(oldList, baseDatas)).dispatchUpdatesTo(baseListAdapter)
        stopRefresh()
        hideOrShowEmptyView(baseDatas.isEmpty())
        onRefreshComplete()
    }

    open fun onRefreshComplete() {

    }

    private fun hideOrShowEmptyView(hidden: Boolean) {
        if (getEmptyLayoutRes() == 0) return

        if (hidden) {
            if (emptyView == null) {
                emptyView = layoutInflater.inflate(getEmptyLayoutRes(), null)
                base_container.addView(emptyView)
            }

            emptyView?.visibility = View.VISIBLE

        } else {
            emptyView?.visibility = View.GONE
        }
    }

    fun stopRefresh() = smartRefreshLayout.finishRefresh()


    fun stopLoadMore() = smartRefreshLayout.finishLoadMore()

    fun notifyDataSetChange() = recyclerView.adapter?.notifyDataSetChanged()

    abstract fun whenBindViewHolder(holder: VH, item: T?)

     open fun whenBindViewHolder(holder: VH, item: T?, payloads: MutableList<Any>){}

    inner class BaseListAdapter : RecyclerView.Adapter<VH>() {

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
            return reflectCreateViewHolder(
                layoutInflater.inflate(
                    if (viewType == 1) getHeaderLayoutRes() else getLayoutRes(),
                    parent,
                    false
                )
            )
        }

        override fun getItemCount() = baseDatas.size + (if (getHeaderLayoutRes() != 0) 1 else 0)

        override fun onBindViewHolder(holder: VH, position: Int) {
            whenBindViewHolder(
                holder,
                when {
                    getHeaderLayoutRes() == 0 -> baseDatas[holder.adapterPosition]
                    position == 0 -> null
                    else -> baseDatas[holder.adapterPosition - 1]
                }
            )
        }

        override fun onBindViewHolder(holder: VH, position: Int, payloads: MutableList<Any>) {
            whenBindViewHolder(
                holder,
                when {
                    getHeaderLayoutRes() == 0 -> baseDatas[holder.adapterPosition]
                    position == 0 -> null
                    else -> baseDatas[holder.adapterPosition - 1]
                }
                , payloads
            )
        }

        override fun getItemViewType(position: Int): Int {
            if (getHeaderLayoutRes() != 0 && position == 0) return 1
            return 2
        }

    }

    private fun reflectCreateViewHolder(inflate: View?): VH {

        if (viewHolderConstructor == null) {
            val type = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[1]
            viewHolderConstructor = ReflectUtil.getRawType(type).constructors[0]
        }

        return viewHolderConstructor?.newInstance(inflate) as VH
    }


    class DiffCallBackImpl<T>(var oldList: List<T>, var newList: List<T>) : DiffUtil.Callback() {

        override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
            oldList[oldItemPosition]?.equals(newList[newItemPosition]) ?: false

        override fun getOldListSize() = oldList.size

        override fun getNewListSize() = newList.size

        override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
            oldList[oldItemPosition]?.equals(newList[newItemPosition]) ?: false

    }
}